Skip to content

fix: omit uris field when HTTPRoute path type is RegularExpression (backport apache/apisix-ingress-controller#2770)#417

Merged
AlinsRan merged 1 commit into
masterfrom
fix/httproute-regex-uris-null-backport
May 26, 2026
Merged

fix: omit uris field when HTTPRoute path type is RegularExpression (backport apache/apisix-ingress-controller#2770)#417
AlinsRan merged 1 commit into
masterfrom
fix/httproute-regex-uris-null-backport

Conversation

@AlinsRan
Copy link
Copy Markdown
Contributor

@AlinsRan AlinsRan commented May 26, 2026

Problem

When an HTTPRoute uses path.type: RegularExpression, the controller correctly generates a vars entry for regex matching, but never sets uris — leaving it nil. Without omitempty, nil serializes as "uris": null, which is rejected by the APISIX Admin API v3 with HTTP 400:

{"errors":[{"expected":"array","code":"invalid_type","path":["services",0,"routes",0,"uris"],"message":"Invalid input: expected array, received null"}]}

Because the controller sends all services in a single bulk payload, every route in the cluster fails to sync — not just the offending regex route.

Note: simply adding omitempty to the Uris field also fails — APISIX rejects an absent field the same way (received undefined).

Root Cause

In translateGatewayHTTPRouteMatch, the PathMatchRegularExpression case only appends to route.Vars and never sets route.Uris:

// before fix
case gatewayv1.PathMatchRegularExpression:
    // builds vars entry for regex ...
    route.Vars = append(route.Vars, this)
    // route.Uris is never set → nil → serialized as null

Fix

Set route.Uris = []string{"/*"} as a catch-all in the RegularExpression case. APISIX routing first matches by uris, then applies vars conditions — so "/*" lets every path through, while the vars regex does the actual filtering:

// after fix
case gatewayv1.PathMatchRegularExpression:
    // builds vars entry for regex ...
    route.Vars = append(route.Vars, this)
    // "/*" satisfies the Admin API; vars performs the real regex match
    route.Uris = []string{"/*"}

The resulting APISIX route payload for a regex path /lk.* now looks like:

{
  "uris": ["/*"],
  "vars": [["uri", "~~", "/lk.*"]]
}

Example

Apply an HTTPRoute with RegularExpression path type:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: httpbin
spec:
  parentRefs:
  - name: my-gateway
  rules:
  - matches:
    - path:
        type: RegularExpression
        value: /status/[0-9]+
    backendRefs:
    - name: httpbin
      port: 80

Before the fix: all routes across the cluster fail to sync (HTTP 400).
After the fix: the route syncs correctly and requests to /status/200 or /status/404 are forwarded to the backend, while /status/ok (non-numeric) returns 404.

Changes

  • internal/adc/translator/httproute.go: set route.Uris = ["/*"] in the PathMatchRegularExpression case
  • test/e2e/gatewayapi/httproute.go: add HTTPRoute RegularExpression Match e2e test

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

📝 Walkthrough

Walkthrough

The PR adds HTTPRoute RegularExpression path matching support. A translator change assigns a catch-all URI to satisfy APISIX Admin API requirements while using route variables for actual regex filtering. A new end-to-end test validates the behavior for matching and non-matching request paths.

Changes

HTTPRoute regex path matching support

Layer / File(s) Summary
Regex path matching translator logic
internal/adc/translator/httproute.go
PathMatchRegularExpression case now assigns route.Uris to a catch-all ["/*"] array with comments noting that actual regex filtering is performed by route.Vars entries.
Regex path matching end-to-end test
test/e2e/gatewayapi/httproute.go
New test case applies an HTTPRoute using RegularExpression path matching /status/[0-9]+ and asserts that matching requests return their target status codes while non-matching paths return 404 NotFound.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
E2e Test Quality Review ⚠️ Warning PR objectives claim unit test added but it doesn't exist. E2E test present but lacks edge case coverage (single digits, empty segments). No regex-specific unit test for code path. Add unit test for PathMatchRegularExpression verifying Uris=["/*"] and Vars regex. Expand E2E with edge cases like /status/0, /status/, and paths without trailing segments.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding omitempty to the uris field when HTTPRoute path type is RegularExpression, which prevents serialization of null values that break APISIX Admin API.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Check ✅ Passed All 7 security categories reviewed with no issues found. Regex pattern properly handled in Vars, no logging/exposure, no auth/secrets/TLS/resource isolation problems.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/httproute-regex-uris-null-backport

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/adc/translator/httproute_test.go (1)

328-336: ⚡ Quick win

Assert serialized JSON omits uris to cover the actual regression.

Current assertions prove route.Uris is nil, but they don’t verify that marshaling excludes the uris key (the root API breakage path).

Proposed test hardening
 func TestTranslateGatewayHTTPRouteMatchRegexPath(t *testing.T) {
@@
 	assert.Equal(t, adctypes.StringOrSlice{StrVal: "uri"}, route.Vars[0][0])
 	assert.Equal(t, adctypes.StringOrSlice{StrVal: "~~"}, route.Vars[0][1])
 	assert.Equal(t, adctypes.StringOrSlice{StrVal: pathValue}, route.Vars[0][2])
+
+	b, err := json.Marshal(route)
+	require.NoError(t, err)
+	assert.NotContains(t, string(b), `"uris"`)
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/adc/translator/httproute_test.go` around lines 328 - 336, Add a
JSON-marshalling assertion to ensure the serialized route omits the "uris" key:
after the existing checks on route.Uris and route.Vars, marshal the route (using
json.Marshal(route)), assert no error, and assert the resulting JSON string does
not contain the literal `"uris"` (or `"uris":`) so the test covers the
regression where the key is present even if route.Uris is nil; reference the
existing route variable and use json.Marshal and string containment assertion
(e.g., assert.NotContains) to validate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/adc/translator/httproute_test.go`:
- Around line 328-336: Add a JSON-marshalling assertion to ensure the serialized
route omits the "uris" key: after the existing checks on route.Uris and
route.Vars, marshal the route (using json.Marshal(route)), assert no error, and
assert the resulting JSON string does not contain the literal `"uris"` (or
`"uris":`) so the test covers the regression where the key is present even if
route.Uris is nil; reference the existing route variable and use json.Marshal
and string containment assertion (e.g., assert.NotContains) to validate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f90cd354-3a0c-44c4-b050-7894a6825cfa

📥 Commits

Reviewing files that changed from the base of the PR and between fbd0841 and 64dc407.

📒 Files selected for processing (2)
  • api/adc/types.go
  • internal/adc/translator/httproute_test.go

@AlinsRan AlinsRan force-pushed the fix/httproute-regex-uris-null-backport branch from 64dc407 to 69bb168 Compare May 26, 2026 02:34
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 26, 2026

conformance test report - apisix-standalone mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-05-26T05:56:39Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    result: success
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests succeeded.
- core:
    result: partial
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 0
      Passed: 32
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests partially succeeded with 1 test skips. Extended tests partially
    succeeded with 1 test skips.
- core:
    result: partial
    skippedTests:
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 10
      Skipped: 1
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 1 test skips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 26, 2026

conformance test report - apisix mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-05-26T05:57:39Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    result: success
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests succeeded.
- core:
    failedTests:
    - HTTPRouteInvalidBackendRefUnknownKind
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 1
      Passed: 31
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests failed with 1 test failures. Extended tests partially succeeded
    with 1 test skips.
- core:
    result: partial
    skippedTests:
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 10
      Skipped: 1
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 1 test skips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 26, 2026

conformance test report

apiVersion: gateway.networking.k8s.io/v1
date: "2026-05-26T06:13:58Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    failedTests:
    - GatewayModifyListeners
    result: failure
    statistics:
      Failed: 1
      Passed: 11
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests failed with 1 test failures.
- core:
    failedTests:
    - GatewayModifyListeners
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 1
      Passed: 31
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests failed with 1 test failures. Extended tests partially succeeded
    with 1 test skips.
- core:
    failedTests:
    - GatewayModifyListeners
    - TLSRouteSimpleSameNamespace
    result: failure
    statistics:
      Failed: 2
      Passed: 9
      Skipped: 0
  name: GATEWAY-TLS
  summary: Core tests failed with 2 test failures.

When path.type is RegularExpression, the controller sets route.Vars
for regex matching but never sets route.Uris, leaving it nil. This
serializes as "uris": null, which is rejected by APISIX Admin API v3
with HTTP 400 (expected array, received null). Omitting the field via
omitempty also fails with "received undefined".

The correct fix is to set uris to ["/*"] as a catch-all passthrough
so APISIX accepts the route payload, while the vars entry performs the
actual regex filtering against the request URI.

Fixes #2764

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@AlinsRan AlinsRan force-pushed the fix/httproute-regex-uris-null-backport branch from 69bb168 to c7cec75 Compare May 26, 2026 05:42
@AlinsRan AlinsRan merged commit ecbb5d5 into master May 26, 2026
22 of 23 checks passed
@AlinsRan AlinsRan deleted the fix/httproute-regex-uris-null-backport branch May 26, 2026 09:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants